//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// DownloadListCtrl.cpp : implementation file
//

//#include "stdafx.h"
#include "wintypes.h"
#include "emule.h"
#include "DownloadListCtrl.h"
#include "otherfunctions.h" 
#include "updownclient.h"
#include "opcodes.h"
#include "ClientDetailDialog.h"
#include "FileDetailDialog.h"
#include "CommentDialogLst.h"

#include "muuli_wdr.h"

#include "../config.h"

#define SYSCOLOR(x) (wxSystemSettings::GetColour(x))

// bitmaps
#include "pixmaps/client0.ICO.xpm"
#include "pixmaps/client1.ICO.xpm"
#include "pixmaps/client2.ICO.xpm"
#include "pixmaps/client3.ICO.xpm"
#include "pixmaps/client4.ICO.xpm"
#include "pixmaps/compatible.ICO.xpm"
#include "pixmaps/Friend.ico.xpm"
#include "pixmaps/neutral.ico.xpm"
#include "pixmaps/mldonk.ico.xpm"
#include "pixmaps/Rating.ico.xpm"
#include "pixmaps/Rating_bad.ico.xpm"
#include "pixmaps/eDonkeyHybrid.ico.xpm"

//#define DLC_DT_TEXT (DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS)

#define DLC_BARUPDATE 512
#define strcmpi strcasecmp

//temp fix for dragging columns
//#define SetExtendedStyle(X) (CListCtrl::SetExtendedStyle(X))
//#define GetColumnWidth(X)   (CListCtrl::GetColumnWidth(X))

// CDownloadListCtrl

// hmm some parts here might be overkill ;) ...
// oh well at least I can do/draw/paint everything I want in this window now ;)
IMPLEMENT_DYNAMIC_CLASS(CDownloadListCtrl,CMuleListCtrl)

BEGIN_EVENT_TABLE(CDownloadListCtrl,CMuleListCtrl)
  EVT_LIST_COL_END_DRAG(ID_DLOADLIST,CDownloadListCtrl::OnColResize)
  EVT_LIST_COL_CLICK(ID_DLOADLIST,CDownloadListCtrl::OnColumnClick)
  EVT_LIST_ITEM_ACTIVATED(ID_DLOADLIST,CDownloadListCtrl::OnLvnItemActivate)
  EVT_LIST_ITEM_RIGHT_CLICK(ID_DLOADLIST,CDownloadListCtrl::OnNMRclick)
END_EVENT_TABLE()

// not like this
/*
  EVT_MENU(MP_PRIOHIGH,CDownloadListCtrl::OnPriHigh)
  EVT_MENU(MP_PRIOLOW,CDownloadListCtrl::OnPriLow)
  EVT_MENU(MP_PRIONORMAL,CDownloadListCtrl::OnPriNormal)
  EVT_MENU(MP_CANCEL,CDownloadListCtrl::OnMCancel)
*/

 void preloadImages(wxImageList* imgs)
{
  imgs->Add(wxBitmap(client0_ICO));
  imgs->Add(wxBitmap(client1_ICO));
  imgs->Add(wxBitmap(client2_ICO));
  imgs->Add(wxBitmap(client3_ICO));
  imgs->Add(wxBitmap(client4_ICO));
  imgs->Add(wxBitmap(compatible_ICO));
  imgs->Add(wxBitmap(Friend_ico));
  imgs->Add(wxBitmap(neutral_ico));
  imgs->Add(wxBitmap(mldonk_ico));
  imgs->Add(wxBitmap(Rating_ico));
  imgs->Add(wxBitmap(Rating_bad_ico));
  imgs->Add(wxBitmap(eDonkeyHybrid_ico));
}

//IMPLEMENT_DYNAMIC(CDownloadListCtrl, CListBox)
CDownloadListCtrl::CDownloadListCtrl()
  : m_ImageList(170,16)
{
	//m_ImageList=new wxImageList(colWidth,16);
	SetImageList(&m_ImageList,wxIMAGE_LIST_SMALL);

	preloadImages(&m_ImageList);
}

CDownloadListCtrl::CDownloadListCtrl(wxWindow*& parent,int id,const wxPoint& pos,wxSize siz,int flags)
  : CMuleListCtrl(parent,id,pos,siz,flags|wxLC_OWNERDRAW),
  m_ImageList(170,16)
{
  //m_ImageList=new wxImageList(colWidth,16);
  m_ClientMenu=NULL;
  m_PrioMenu=NULL;
  m_FileMenu=NULL;

  wxColour col=wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
  wxColour newcol=wxColour(G_BLEND(col.Red(),125),
			   G_BLEND(col.Green(),125),
			   G_BLEND(col.Blue(),125));
  m_hilightBrush=new wxBrush(newcol,wxSOLID);

  col=wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW);
  newcol=wxColour(G_BLEND(col.Red(),125),
		  G_BLEND(col.Green(),125),
		  G_BLEND(col.Blue(),125));
  m_hilightUnfocusBrush=new wxBrush(newcol,wxSOLID);

  SetImageList(&m_ImageList,wxIMAGE_LIST_SMALL);
  preloadImages(&m_ImageList);
  Init();
}

void CDownloadListCtrl::InitSort()
{
  LoadSettings(CPreferences::tableDownload);
  // Barry - Use preferred sort order from preferences
  int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableDownload);
  bool sortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableDownload);
  SetSortArrow(sortItem, sortAscending);
  SortItems(SortProc, sortItem + (sortAscending ? 0:100));	
}

CDownloadListCtrl::~CDownloadListCtrl(){
        while(m_ListItems.empty() == false){
                delete m_ListItems.begin()->second; // second = CtrlItem_Struct*
                m_ListItems.erase(m_ListItems.begin());
        }
	delete m_hilightBrush;
	delete m_hilightUnfocusBrush;
}

#include <wx/dcmemory.h>

void CDownloadListCtrl::HideSources(CPartFile* toCollapse,bool isShift,bool isCtrl,bool isAlt) {
	int pre,post;
	pre = post = 0;
	Freeze();
	for(int i = 0; i < GetItemCount(); i++) {
		CtrlItem_Struct* item = (CtrlItem_Struct*)this->GetItemData(i);
		if(item->owner == toCollapse) {
			pre++;
			if(isShift || isCtrl || isAlt){
				uint8 ds=((CUpDownClient*)item->value)->GetDownloadState();
				if((isShift && ds==DS_DOWNLOADING) ||
					(isCtrl && ((CUpDownClient*)item->value)->GetRemoteQueueRank()> 0) ||
					(isAlt && ds!=DS_NONEEDEDPARTS)) continue;
			}
			item->dwUpdated = 0;
			//item->status.DeleteObject();
			if(item->status) delete item->status;
			item->status=NULL; // clear it!!!!
			DeleteItem(i--);
			post++;
		}
	}
	if (pre-post==0) toCollapse->srcarevisible = false;
	Thaw();
}

void CDownloadListCtrl::collectSelections(CTypedPtrList<CPtrList,CPartFile*>* selectedList)
{
  long item=-1;
  for(;;) {
    item=GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
    if(item==-1)
      break;
    if(((CtrlItem_Struct*)this->GetItemData(item))->type==1)
      selectedList->Append((CPartFile*)((CtrlItem_Struct*)this->GetItemData(item))->value);
  }
}

void CDownloadListCtrl::setPri(int newpri)
{
  CTypedPtrList<CPtrList,CPartFile*> selectedList;
  collectSelections(&selectedList);
  long item=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
  if(item!=-1) {
    CtrlItem_Struct* content=(CtrlItem_Struct*)this->GetItemData(item);
    UINT selectedCount=this->GetSelectedItemCount();

    if(content->type==1) {
      CPartFile* file=(CPartFile*)content->value;
      if(selectedCount>1) {
	while(!selectedList.IsEmpty()) {
	  selectedList.GetHead()->SetPriority(newpri);
	  selectedList.RemoveHead();
	}
	return;
      }
      file->SetPriority(newpri);
    }
  }
}

void CDownloadListCtrl::OnMCancel(wxCommandEvent& evt)
{
  CTypedPtrList<CPtrList,CPartFile*> selectedList;
  collectSelections(&selectedList);
  long item=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
  if(item!=-1) {
    CtrlItem_Struct* content=(CtrlItem_Struct*)this->GetItemData(item);
    UINT selectedCount=this->GetSelectedItemCount();
    if(content->type==1) {
      CPartFile* file=(CPartFile*)content->value;
      if(selectedCount>1) {
	wxString fileList; 
	for (POSITION pos = selectedList.GetHeadPosition() ; pos != 0 ; selectedList.GetNext(pos))
	  {
	    fileList += "\n" ; 
	    fileList += selectedList.GetAt(pos)->GetFileName(); 
	  } 
	if (wxMessageBox((GetResString(IDS_Q_CANCELDL).GetData() + fileList),GetResString(IDS_CANCEL),wxICON_QUESTION|wxYES_NO) == wxYES)
	  { 
	    while(!selectedList.IsEmpty())
	      {
		switch(selectedList.GetHead()->GetStatus()) {
		case PS_WAITINGFORHASH: 
		case PS_HASHING: 
		case PS_COMPLETING: 
		case PS_COMPLETE: 
		  selectedList.RemoveHead(); 
		  break; 
		default: 
		  selectedList.GetHead()->DeleteFile(); 
		  selectedList.RemoveHead(); 
		}
	      }  
	  }
	return;
      }
      // single selection
      switch(file->GetStatus())
	{
	case PS_WAITINGFORHASH:
	case PS_HASHING:
	case PS_COMPLETING:
	case PS_COMPLETE:
	  break;
	default:
	  if (wxMessageBox(GetResString(IDS_Q_CANCELDL2).GetData(),GetResString(IDS_CANCEL),wxICON_QUESTION|wxYES_NO) == wxYES)
	    file->DeleteFile();
	  break;
	}
    }
  }
}

void CDownloadListCtrl::OnPriLow(wxCommandEvent& evt)
{
  setPri(PR_LOW);
}

void CDownloadListCtrl::OnPriNormal(wxCommandEvent& evt)
{
  setPri(PR_NORMAL);
}

void CDownloadListCtrl::OnPriHigh(wxCommandEvent& evt)
{
  setPri(PR_HIGH);
}

// laziness strikes
#define MF_CHECKED TRUE
#define MF_UNCHECKED FALSE
#define MF_ENABLED TRUE
#define MF_GRAYED FALSE

void CDownloadListCtrl::OnNMRclick(wxListEvent& evt)
{
    // Check if clicked item is selected. If not, unselect all and select it.
  long item=-1;
  if (!GetItemState(evt.GetIndex(), wxLIST_STATE_SELECTED)) {
    for (;;) {
      item = GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
      if (item==-1) break;
      SetItemState(item, 0, wxLIST_STATE_SELECTED);
    }
    SetItemState(evt.GetIndex(), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
  }

  item=-1;
  for(;;) {
    item=GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
    if(item==-1)
      break;
    CtrlItem_Struct* content=(CtrlItem_Struct*)this->GetItemData(item);
    if(content->type==1) {
      CPartFile* file=(CPartFile*)content->value;
      if(m_PrioMenu==NULL) {
	wxMenu* priomenu=new wxMenu();
	priomenu->AppendCheckItem(MP_PRIOLOW,GetResString(IDS_PRIOLOW));
	priomenu->AppendCheckItem(MP_PRIONORMAL,GetResString(IDS_PRIONORMAL));
	priomenu->AppendCheckItem(MP_PRIOHIGH,GetResString(IDS_PRIOHIGH));
	priomenu->AppendCheckItem(MP_PRIOAUTO,GetResString(IDS_PRIOAUTO));
	m_PrioMenu=priomenu;

	wxMenu* menu=new wxMenu(GetResString(IDS_DOWNLOADMENUTITLE));
	menu->Append(999989,GetResString(IDS_PRIORITY),priomenu);
	menu->Append(MP_CANCEL,GetResString(IDS_MAIN_BTN_CANCEL));
	menu->Append(MP_STOP,GetResString(IDS_DL_STOP));
	menu->Append(MP_PAUSE,GetResString(IDS_DL_PAUSE));
	menu->Append(MP_RESUME,GetResString(IDS_DL_RESUME));
	menu->AppendSeparator();
	menu->Append(MP_OPEN,GetResString(IDS_DL_OPEN));
	menu->Append(MP_PREVIEW,GetResString(IDS_DL_PREVIEW));
	menu->Append(MP_METINFO,GetResString(IDS_DL_INFO));
	menu->Append(MP_VIEWFILECOMMENTS,GetResString(IDS_CMT_SHOWALL));
	menu->AppendSeparator();
	menu->Append(MP_CLEARCOMPLETED,GetResString(IDS_DL_CLEAR));
	menu->Append(MP_GETED2KLINK,GetResString(IDS_DL_LINK1));
	menu->Append(MP_GETHTMLED2KLINK,GetResString(IDS_DL_LINK2));
	m_FileMenu=menu;
      }

      // then set state
      wxMenu*menu =m_FileMenu;
      menu->Enable(MP_PAUSE,((file->GetStatus() != PS_PAUSED && file->GetStatus() != PS_ERROR) ? MF_ENABLED:MF_GRAYED));
      menu->Enable(MP_STOP,((file->GetStatus() != PS_PAUSED && file->GetStatus() != PS_ERROR) ? MF_ENABLED:MF_GRAYED));
      menu->Enable(MP_RESUME,((file->GetStatus() == PS_PAUSED) ? MF_ENABLED:MF_GRAYED));
      menu->Enable(MP_OPEN,((file->GetStatus() == PS_COMPLETE) ? MF_ENABLED:MF_GRAYED)); //<<--9/21/02
      menu->Enable(MP_PREVIEW,((file->PreviewAvailable()) ? MF_ENABLED:MF_GRAYED));
      
      menu->Enable(MP_CANCEL,MF_ENABLED);
      
      wxMenu*priomenu=m_PrioMenu;
      priomenu->Check(MP_PRIOHIGH,(file->GetPriority() == PR_HIGH)? MF_CHECKED:MF_UNCHECKED);
      priomenu->Check(MP_PRIONORMAL,(file->GetPriority() == PR_NORMAL)? MF_CHECKED:MF_UNCHECKED);
      priomenu->Check(MP_PRIOLOW,(file->GetPriority() == PR_LOW)? MF_CHECKED:MF_UNCHECKED);

      PopupMenu(m_FileMenu,evt.GetPoint());
    } else {
      if(m_ClientMenu==NULL) {
	wxMenu* menu=new wxMenu(GetResString(IDS_CLIENTS));
	menu->Append(MP_DETAIL,GetResString(IDS_SHOWDETAILS));
	menu->Append(MP_ADDFRIEND,GetResString(IDS_ADDFRIEND));
	menu->Append(MP_MESSAGE,GetResString(IDS_SEND_MSG));
	menu->Append(MP_SHOWLIST,GetResString(IDS_VIEWFILES));
	m_ClientMenu=menu;
      } 
      PopupMenu(m_ClientMenu,evt.GetPoint());
    }
    // make sure that we terminate
    break;
  }
  if(item==-1) {
    // no selection.. actually this event won't get fired in this case so 
    // do nothing..
  }
}

void CDownloadListCtrl::OnColResize(wxListEvent& evt)
{
  return;
}

void CDownloadListCtrl::OnDrawItem(int item,wxDC* dc,const wxRect& rect,const wxRect& rectHL,bool highlighted)
{
  CtrlItem_Struct* content=(CtrlItem_Struct*)GetItemData(item);

  //if ((content->type == 1) && (lpDrawItemStruct->itemAction | ODA_SELECT) &&
  //    (lpDrawItemStruct->itemState & ODS_SELECTED)) {
  if ((content->type == 1) && (highlighted) ) {   
    if(GetFocus()) {
      dc->SetBackground(*m_hilightBrush);
      dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
    } else {
      dc->SetBackground(*m_hilightUnfocusBrush);
      dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
    }
  } /*else if (content->type != 1) {
      COLORREF crHighlight = m_crWindowText;
      COLORREF crWindow = m_crWindow;
      COLORREF crHalf = DLC_RGBBLEND(crHighlight, crWindow, 16);
      odc->SetBkColor(crHalf);
      }*/ else {
	//dc->SetBkColor(GetBkColor());
	dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX),wxSOLID)));
	dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
      }
  // we must fill the background
  //if(content->type==1) {
  wxPen mypen;
  if(content->type==FILE_TYPE && highlighted) {
    // set pen so that we'll get nice border
    wxColour old=GetFocus()?m_hilightBrush->GetColour():m_hilightUnfocusBrush->GetColour();
    wxColour newcol=wxColour(((int)old.Red()*65)/100,
			     ((int)old.Green()*65)/100,
			     ((int)old.Blue()*65)/100);
    mypen=wxPen(newcol,1,wxSOLID);
    dc->SetPen(mypen);
  } else {
    if(content->type!=FILE_TYPE && highlighted) {
      wxColour old=m_hilightBrush->GetColour();
      wxColour newcol=wxColour(((int)old.Red()*65)/100,
			       ((int)old.Green()*65)/100,
			       ((int)old.Blue()*65)/100);
      mypen=wxPen(newcol,1,wxSOLID);
      dc->SetPen(mypen);
    } else {
      dc->SetPen(*wxTRANSPARENT_PEN);
    }
  }
    dc->SetBrush(dc->GetBackground());
    dc->DrawRectangle(rectHL);
    dc->SetPen(*wxTRANSPARENT_PEN);
  //}

  RECT cur_rec;
  int tree_start=0,tree_end=0;
  BOOL notLast = item + 1 != GetItemCount();
  BOOL notFirst = item != 0;

  cur_rec.left=rect.x;
  cur_rec.top=rect.y;
  cur_rec.right=rect.x+rect.width;
  cur_rec.bottom=rect.y+rect.height;

  int iOffset = 4; //dc->GetTextExtent(_T(" "), 1 ).cx*2;
  //CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
  int iCount = GetColumnCount(); //pHeaderCtrl->GetItemCount();

  cur_rec.right=cur_rec.left;
  cur_rec.right-=iOffset;
  cur_rec.left+=iOffset;

  if(content->type==1) {
    for(int iCurrent = 0; iCurrent < iCount; iCurrent++) {
      
      int iColumn = iCurrent; //pHeaderCtrl->OrderToIndex(iCurrent);
      //int cx = GetColumnWidth(iColumn);
      wxListItem listitem;
      GetColumn(iColumn,listitem);
      int cx=listitem.GetWidth();
      if(iColumn == 5) {
	int iNextLeft = cur_rec.left + cx;
	//set up tree vars
	cur_rec.left = cur_rec.right + iOffset;
	cur_rec.right = cur_rec.left + min(8, cx);
	tree_start = cur_rec.left + 1;
	tree_end = cur_rec.right;
	//normal column stuff
	cur_rec.left = cur_rec.right + 1;
	cur_rec.right = tree_start + cx - iOffset;
	//printf("**** Fileitem:");
	DrawFileItem(dc, 5, &cur_rec, content);
	cur_rec.left = iNextLeft;
      } else {
	cur_rec.right += cx;
	cur_rec.top+=3;// will ensure that text is about in the middle ;)
	DrawFileItem(dc, iColumn, &cur_rec, content);
	cur_rec.top-=3;
	cur_rec.left += cx;
      }      
    }
  } else if (content->type == 3 || content->type == 2){
    
    for(int iCurrent = 0; iCurrent < iCount; iCurrent++) {
      
      int iColumn = iCurrent;//pHeaderCtrl->OrderToIndex(iCurrent);
      //int cx = GetColumnWidth(iColumn);
      wxListItem listitem;
      GetColumn(iColumn,listitem);
      int cx=listitem.GetWidth();

      if(iColumn == 5) {
	int iNextLeft = cur_rec.left + cx;
	//set up tree vars
	cur_rec.left = cur_rec.right + iOffset;
	cur_rec.right = cur_rec.left + min(8, cx);
	tree_start = cur_rec.left + 1;
	tree_end = cur_rec.right;
	//normal column stuff
	cur_rec.left = cur_rec.right + 1;
	cur_rec.right = tree_start + cx - iOffset;
	DrawSourceItem(dc, 5, &cur_rec, content);
	cur_rec.left = iNextLeft;
      } else {
	while(iCurrent < iCount) {
	  int iNext = iCurrent+1; //pHeaderCtrl->OrderToIndex(iCurrent + 1);
	  if(iNext == 1 /*|| iNext == 5 || iNext == 7 || iNext == 8*/) {
	    wxListItem newlistitem;
	    GetColumn(iNext,newlistitem);
	    cx += newlistitem.GetWidth();//GetColumnWidth(iNext);
	  } else
	    break;
	  iCurrent++;
	}
	cur_rec.right += cx;
	cur_rec.top+=3; // will ensure that text is about in the middle ;)
	DrawSourceItem(dc, iColumn, &cur_rec, content);
	cur_rec.top-=3;
	cur_rec.left += cx;
      }
    }
  }

  // piirrusteluja puuttuu (selection rectanglet)

  //draw tree last so it draws over selected and focus (looks better)
  if(tree_start < tree_end) {
    //set new bounds
    RECT tree_rect;
    tree_rect.top    = rect.y;//lpDrawItemStruct->rcItem.top;
    tree_rect.bottom = rect.y+rect.height; //lpDrawItemStruct->rcItem.bottom;
    tree_rect.left   = tree_start;
    tree_rect.right  = tree_end;
    // TODO:varmaanki clipper?
    //dc->SetBoundsRect(&tree_rect, DCB_DISABLE);

    //gather some information
    BOOL hasNext = notLast &&
      ((CtrlItem_Struct*)this->GetItemData(item + 1))->type != 1;
    BOOL isOpenRoot = hasNext && content->type == 1;
    BOOL isChild = content->type != 1;
    BOOL isExpandable = !isChild && ((CPartFile*)content->value)->GetSourceCount() > 0;
    //might as well calculate these now
    int treeCenter = tree_start + 3;
    int middle = (cur_rec.top + cur_rec.bottom + 1) / 2;
    
    //set up a new pen for drawing the tree
    wxPen pn, *oldpn;
    //pn.CreatePen(PS_SOLID, 1, dc->GetTextColor());
    pn=*(wxThePenList->FindOrCreatePen(dc->GetTextForeground(),1,wxSOLID));
    //oldpn = dc->SelectObject(&pn);
    dc->SetPen(pn);
    
    if(isChild) {
      //draw the line to the status bar
      //dc->MoveTo(tree_end, middle);
      //dc->LineTo(tree_start + 3, middle);
      dc->DrawLine(tree_end,middle,tree_start+3,middle);
      
      //draw the line to the child node
      if(hasNext) {
	//dc->MoveTo(treeCenter, middle);
	//dc->LineTo(treeCenter, cur_rec.bottom + 1);
	dc->DrawLine(treeCenter,middle,treeCenter,cur_rec.bottom+1);
      }
    } else if(isOpenRoot) {
      //draw circle
      RECT circle_rec;
      //COLORREF crBk = dc->GetBkColor();
      wxColour crBk=dc->GetBackground().GetColour();
      circle_rec.top    = middle - 2;
      circle_rec.bottom = middle + 3;
      circle_rec.left   = treeCenter - 2;
      circle_rec.right  = treeCenter + 3;
      dc->DrawLine(circle_rec.left,circle_rec.top,circle_rec.right,circle_rec.top);
      dc->DrawLine(circle_rec.right,circle_rec.top,circle_rec.right,circle_rec.bottom);
      dc->DrawLine(circle_rec.right,circle_rec.bottom,circle_rec.left,circle_rec.bottom);
      dc->DrawLine(circle_rec.left,circle_rec.bottom,circle_rec.left,circle_rec.top);
      //dc->FrameRect(&circle_rec, &CBrush(dc->GetTextColor()));
      //dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(dc->GetTextForeground(),wxSOLID)));

      wxPen oldpen=dc->GetPen();
      dc->SetPen(*(wxThePenList->FindOrCreatePen(crBk,1,wxSOLID)));
      dc->DrawPoint(circle_rec.left,circle_rec.top);
      dc->DrawPoint(circle_rec.right,circle_rec.top);
      dc->DrawPoint(circle_rec.left,circle_rec.bottom);
      dc->DrawPoint(circle_rec.right,circle_rec.bottom);
      dc->SetPen(oldpen);
#if 0
      dc->SetPixelV(circle_rec.left,      circle_rec.top,    crBk);
      dc->SetPixelV(circle_rec.right - 1, circle_rec.top,    crBk);
      dc->SetPixelV(circle_rec.left,      circle_rec.bottom - 1, crBk);
      dc->SetPixelV(circle_rec.right - 1, circle_rec.bottom - 1, crBk);
#endif

      //draw the line to the child node
      if(hasNext) {
	//dc->MoveTo(treeCenter, middle + 3);
	//dc->LineTo(treeCenter, cur_rec.bottom + 1);
	dc->DrawLine(treeCenter,middle+3,treeCenter,cur_rec.bottom+1);
	
      }
    } /*else if(isExpandable) {
      //draw a + sign
      dc->MoveTo(treeCenter, middle - 2);
      dc->LineTo(treeCenter, middle + 3);
      dc->MoveTo(treeCenter - 2, middle);
      dc->LineTo(treeCenter + 3, middle);
      }*/
    
    //draw the line back up to parent node
    if(notFirst && isChild) {
      //dc->MoveTo(treeCenter, middle);
      //dc->LineTo(treeCenter, cur_rec.top - 1);
      dc->DrawLine(treeCenter,middle,treeCenter,cur_rec.top-1);
    }
    
    //put the old pen back
    //dc->SelectObject(oldpn);
    //pn.DeleteObject();
  }
}

void CDownloadListCtrl::Init(){
#if 0
	CImageList ilDummyImageList; //dummy list
	ilDummyImageList.Create(1, 17,ILC_COLOR, 1, 1); 
	SetImageList(&ilDummyImageList, LVSIL_SMALL);
	SetStyle();
	SetColors();

	ModifyStyle(LVS_SINGLESEL,0);
#endif

#define LVCFMT_LEFT wxLIST_FORMAT_LEFT

	InsertColumn(0,GetResString(IDS_DL_FILENAME),LVCFMT_LEFT, 260);
	InsertColumn(1,GetResString(IDS_DL_SIZE),LVCFMT_LEFT, 60);
	InsertColumn(2,GetResString(IDS_DL_TRANSF),LVCFMT_LEFT, 65);
	InsertColumn(3,GetResString(IDS_DL_TRANSFCOMPL),LVCFMT_LEFT, 65);
	InsertColumn(4,GetResString(IDS_DL_SPEED),LVCFMT_LEFT, 65);
	InsertColumn(5,GetResString(IDS_DL_PROGRESS),LVCFMT_LEFT, 170);
	InsertColumn(6,GetResString(IDS_DL_SOURCES),LVCFMT_LEFT, 50);
	InsertColumn(7,GetResString(IDS_PRIORITY),LVCFMT_LEFT, 55);
	InsertColumn(8,GetResString(IDS_STATUS),LVCFMT_LEFT, 70);
	InsertColumn(9,GetResString(IDS_DL_REMAINS),LVCFMT_LEFT, 110);
        CString lsctitle=GetResString(IDS_LASTSEENCOMPL);
        lsctitle.Replace(":","");
        InsertColumn(10, lsctitle,LVCFMT_LEFT, 220);
        lsctitle=GetResString(IDS_FD_LASTCHANGE);
        lsctitle.Replace(":","");
        InsertColumn(11, lsctitle,LVCFMT_LEFT, 220);

	curTab=0;
#if 0
	m_ImageList.Create(15,15,ILC_COLOR32|ILC_MASK,0,10);
	m_ImageList.SetBkColor(CLR_NONE);
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS1));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS2));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS3));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS4));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS5));
	m_ImageList.Add(theApp.LoadIcon(IDI_COMPPROT));

	CreateMenues();
#endif
	// sort setting -> InitSort
}

void CDownloadListCtrl::Localize() {
#if 0
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	HDITEM hdi;
	hdi.mask = HDI_TEXT;
	CString strRes;

	strRes = GetResString(IDS_DL_FILENAME);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(0, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SIZE);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(1, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_TRANSF);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(2, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SPEED);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(3, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_PROGRESS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(4, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SOURCES);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(5, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_PRIORITY);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(6, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_STATUS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(7, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_REMAINS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(8, &hdi);
	strRes.ReleaseBuffer();
#endif
}


void CDownloadListCtrl::AddFile(CPartFile* toadd){
        CtrlItem_Struct* newitem = new CtrlItem_Struct;
        uint16 itemnr = GetItemCount();
        newitem->owner = NULL;
        newitem->type = FILE_TYPE;
        newitem->value = toadd;
	newitem->status=NULL;
        newitem->parent = NULL;
	newitem->dwUpdated=0;
		//listcontent.Append(newitem);	
	m_ListItems.insert(ListItemsPair(toadd,newitem));
        //InsertItem(LVIF_PARAM,itemnr,0,0,0,0,(LPARAM)newitem);
	  
	if(ShowItemInCurrentCat(toadd,curTab)) { 
	  // rip something off from DrawFileItem()
	  CPartFile* pf=(CPartFile*)newitem->value;
	  uint32 newid=InsertItem(itemnr,pf->GetFileName());
	  SetItemData(newid,(long)newitem);

	  wxListItem myitem;
	  myitem.m_itemId=newid;

	  myitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
	  SetItem(myitem);
	}
	ShowFilesCount();
}

void CDownloadListCtrl::AddSource(CPartFile* owner,CUpDownClient* source,bool notavailable){
  CtrlItem_Struct* newitem = new CtrlItem_Struct;
  newitem->owner = owner;
  newitem->type = (notavailable)? UNAVAILABLE_SOURCE:AVAILABLE_SOURCE;
  newitem->value = source;
  newitem->status=NULL;
  newitem->dwUpdated = 0; 

  //listcontent.Append(newitem);
  // Update cross link to the owner
  ListItems::const_iterator ownerIt = m_ListItems.find(owner);
  //ASSERT(ownerIt != m_ListItems.end());
  CtrlItem_Struct* ownerItem = ownerIt->second;
  //ASSERT(ownerItem->value == owner);
  newitem->parent = ownerItem;
  
                // The same source could be added a few time but only one time per file 
                {
                        // Update the other instances of this source
                        bool bFound = false;
                        std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(source);
                        for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
                                CtrlItem_Struct* cur_item = it->second;

                                // Check if this source has been already added to this file => to be sure
                                if(cur_item->owner == owner){
                                        // Update this instance with its new setting
                                        cur_item->type = newitem->type;
                                        cur_item->dwUpdated = 0;
                                        bFound = true;
                                }
                                else if(notavailable == false){
                                        // The state 'Available' is exclusive
                                        cur_item->type = UNAVAILABLE_SOURCE;
                                        cur_item->dwUpdated = 0;
                                }
                        }
                       if(bFound == true){
                                delete newitem; 
                                return;
                        }
                }
                m_ListItems.insert(ListItemsPair(source, newitem));

		if(!owner->srcarevisible)
		  return;

		// insert newitem to the display too!
		// find it
		int itemnr=FindItem(-1,(long)ownerItem);
		while(GetItemCount()>itemnr+1 && ((CtrlItem_Struct*)GetItemData(itemnr+1))->type!=FILE_TYPE)
		  itemnr++;
		
		int newid=InsertItem(itemnr+1,"This text is not visible");
		SetItemData(newid,(long)newitem);
		
		// background.. this should be in a function
		wxListItem newitemL;
		newitemL.m_itemId=newid;

		newitemL.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
		SetItem(newitemL);
		
}


void CDownloadListCtrl::RemoveSource(CUpDownClient* source,CPartFile* owner){
	// Retrieve all entries matching the source
	std::pair<ListItems::iterator, ListItems::iterator> rangeIt = m_ListItems.equal_range(source);
	for(ListItems::iterator it = rangeIt.first; it != rangeIt.second; ){
		CtrlItem_Struct* delItem  = it->second;
		if(owner == NULL || owner == delItem->owner){
			// Remove it from the m_ListItems			
		  ListItems::iterator tmp=it; it++;
		  /*it =*/ m_ListItems.erase(tmp);

			//LVFINDINFO find;
			//find.flags = LVFI_PARAM;
			//find.lParam = (LPARAM)delitem;
			//sint16 result = FindItem(&find);
			sint16 result=FindItem(-1,(long)delItem);
			if (result != (-1))
			  DeleteItem(result);
			delete delItem;
		} else {
		  it++;
		}
	}
}

// argh. wxWin lists. remove these!!!
void CDownloadListCtrl::RemoveFile(CPartFile* toremove){
	// Retrieve all entries matching the File or linked to the file
	// Remark: The 'asked another files' clients must be removed from here
	//ASSERT(toremove != NULL);
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* delItem = it->second;
		if(delItem->owner == toremove || delItem->value == toremove){
			// Remove it from the m_ListItems
		  ListItems::iterator tmp=it; it++;
		  /*it =*/ m_ListItems.erase(tmp);

			// Remove it from the CListCtrl
			//LVFINDINFO find;
			//find.flags = LVFI_PARAM;
			//find.lParam = (LPARAM)delItem;
			sint16 result = FindItem(-1,(long)delItem);
			  
			if(result != (-1)){
				DeleteItem(result);
			}

			// finally it could be delete
			delete delItem;
		}
		else {
			it++;
		}
	}
	ShowFilesCount();
}

void CDownloadListCtrl::UpdateItem(void* toupdate) {
	// Retrieve all entries matching the source
	std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(toupdate);
	for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
		CtrlItem_Struct* updateItem  = it->second;

		// Find entry in CListCtrl and update object
 		//LVFINDINFO find;
		//find.flags = LVFI_PARAM;
		//find.lParam = (LPARAM)updateItem;
		sint16 result = FindItem(-1,(long)updateItem);
		if(result != (-1)){
			updateItem->dwUpdated = 0;
			long first=0,last=0;
			GetVisibleLines(&first,&last);
			if(result>=first && result<=last) {
			  RefreshItems(result,result);
			}
		}
	}
}

void CDownloadListCtrl::DrawFileItem(wxDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem) {
  if(lpRect->left < lpRect->right) {
    
    // force clipper (clip 2 px more than the rectangle from the right side)
    wxDCClipper clipper(*dc,lpRect->left,lpRect->top,lpRect->right-lpRect->left-2,lpRect->bottom-lpRect->top);

    CString buffer;
    CPartFile *lpPartFile = (CPartFile*)lpCtrlItem->value;
    switch(nColumn) {
      
    case 0:{		// file name
      LPRECT temprec=lpRect;
      if (lpPartFile->HasComment() || lpPartFile->HasRating()){
	
	int image=9;
	if (lpPartFile->HasRating()) if (lpPartFile->HasBadRating()) image=10;
	
	// it's already centered by OnDrawItem() ... 
	POINT point= {lpRect->left-4,lpRect->top/*+3*/};
	//m_ImageList.Draw(dc, image, point, ILD_NORMAL);
	m_ImageList.Draw(image,*dc,point.x,point.y-1,wxIMAGELIST_DRAW_TRANSPARENT);
	lpRect->left+=9;
	dc->DrawText(lpPartFile->GetFileName(), lpRect->left,lpRect->top);
	lpRect->left-=9;
      } else
	dc->DrawText(lpPartFile->GetFileName(), lpRect->left,lpRect->top);
    }
      break;
      
    case 1:		// size
      buffer.Format("%s",CastItoXBytes(lpPartFile->GetFileSize()).GetData());
      //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
      
    case 2:		// transfered
      buffer.Format( "%s", CastItoXBytes(lpPartFile->GetTransfered()).GetData());
      //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);	
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
      
    case 3:		// transfered complete
      buffer.Format( "%s", CastItoXBytes(lpPartFile->GetCompletedSize()).GetData());
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;

    case 4:		// speed
      if (lpPartFile->GetTransferingSrcCount()==0)
	buffer="";
      else
	buffer.Format("%.1f %s", lpPartFile->GetDatarate() / 1024.0f,GetResString(IDS_KBYTESEC).GetData());
      
      //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
      
#ifndef DISABLE_PROGRESS
    case 5: // progress
      {
	lpRect->bottom --;
	lpRect->top ++;
	int  iWidth = lpRect->right - lpRect->left; 
	int iHeight = lpRect->bottom - lpRect->top;

	// DO NOT DRAW IT ALL THE TIME
	DWORD dwTicks=GetTickCount();
	wxMemoryDC cdcStatus;

	if(lpCtrlItem->dwUpdated+DLC_BARUPDATE<dwTicks||!lpCtrlItem->status||iWidth!=lpCtrlItem->status->GetWidth()||!lpCtrlItem->dwUpdated) {
	  if(lpCtrlItem->status==NULL) {
	    lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
	  } else {
	    //delete lpCtrlItem->status;
	    //lpCtrlItem->status=NULL;
	    //lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
	    lpCtrlItem->status->Create(iWidth,iHeight);//SetWidth(iWidth);
	  }
	  //lpCtrlItem->status->Create(iWidth,iHeight);
	  cdcStatus.SelectObject(*(lpCtrlItem->status));
	  
	  lpPartFile->DrawStatusBar(&cdcStatus,wxRect(0,0,iWidth,iHeight),
	  		    theApp.glob_prefs->UseFlatBar());
	  lpCtrlItem->dwUpdated=dwTicks+(rand()%128);

	} else {
	  cdcStatus.SelectObject(*(lpCtrlItem->status));
	}

	dc->Blit(lpRect->left,lpRect->top,iWidth,iHeight,&cdcStatus,0,0);
	cdcStatus.SelectObject(wxNullBitmap);

	lpRect->bottom++;
	lpRect->top--;
      }
      break;
#endif					    
      
    case 6:		// sources
      {
	CString buffer2;
	uint16 ncsc=lpPartFile->GetNotCurrentSourcesCount();
	if(ncsc>0) buffer2.Format("%i/",lpPartFile->GetSourceCount() -ncsc); else buffer2="";
	buffer.Format("%s%i (%i)",buffer2.GetData(),
		      lpPartFile->GetSourceCount(),
		      lpPartFile->GetTransferingSrcCount());
	//UAP
	if (((CKnownFile*)lpPartFile)->IsAutoPrioritized())
	  ((CKnownFile*)lpPartFile)->UpdateUploadAutoPriority() ;
	theApp.sharedfiles->UpdateItem(((CKnownFile*)lpPartFile)) ;
	//end UAP
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
      break;
      
    case 7:		// prio
      if ((lpPartFile->IsAutoPrioritized()) && (theApp.glob_prefs->GetNewAutoUp())) { //Hunter
	if ((lpPartFile->GetSourceCount() <= 100) && (lpPartFile->GetPriority() != PR_HIGH)) {
	  lpPartFile->SetPriority(PR_HIGH);
	} else if ((lpPartFile->GetSourceCount() > 100) && (lpPartFile->GetSourceCount() <= 300) && (lpPartFile->GetPriority() != PR_NORMAL)) {
	  lpPartFile->SetPriority(PR_NORMAL);
	} else if ((lpPartFile->GetSourceCount() > 300) && (lpPartFile->GetPriority() != PR_LOW)) {
	  lpPartFile->SetPriority(PR_LOW);
	}
	
	switch(lpPartFile->GetPriority()) {
	case PR_LOW:
	  dc->DrawText(GetResString(IDS_PRIOAUTOLOW),lpRect->left,lpRect->top);
	  break;
	case PR_NORMAL:
	  dc->DrawText(GetResString(IDS_PRIOAUTONORMAL),lpRect->left,lpRect->top);
	  break;
	case PR_HIGH:
	  dc->DrawText(GetResString(IDS_PRIOAUTOHIGH),lpRect->left,lpRect->top);
	  break;
	}
      } else {
	switch(lpPartFile->GetPriority()) {
	case 0:
	  dc->DrawText(GetResString(IDS_PRIOLOW),lpRect->left,lpRect->top);
	  break;
	case 1:
	  dc->DrawText(GetResString(IDS_PRIONORMAL),lpRect->left,lpRect->top);
	  break;
	case 2:
	  dc->DrawText(GetResString(IDS_PRIOHIGH),lpRect->left,lpRect->top);
	  break;
	}
      }
      break;
      
   case 8:		// <<--9/21/02
     buffer.Format("%s", lpPartFile->getPartfileStatus().GetData());
      //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
      
    case 9:		// remaining time & size
      {
	//char bufferSize[50];
	//char bufferTime[50];
	
	//size 
	uint32 remains;
	remains=lpPartFile->GetFileSize()-lpPartFile->GetCompletedSize();		//<<-- 09/27/2002, CML
	
	if( remains < 0 ) remains = 0;
	
	// time 
	sint32 restTime=lpPartFile->getTimeRemaining();
	buffer.Format("%s (%s)", CastSecondsToHM(restTime).GetData(),CastItoXBytes(remains).GetData());
	if (lpPartFile->GetStatus()==PS_COMPLETING || lpPartFile->GetStatus()==PS_COMPLETE )
	  buffer="";
	
	
	//dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
      break;
      case 10: // last seen complete
	{
	  if (lpPartFile->lastseencomplete==0)
	    buffer=GetResString(IDS_UNKNOWN);
	  else {
	    char tmpstr[512];
	    strftime(tmpstr,sizeof(tmpstr),"%A, %x, %X",
		     localtime(&lpPartFile->lastseencomplete));
	    buffer=tmpstr; //lpPartFile->lastseencomplete.Format( "%A, %x, %X");
	  }
	  dc->DrawText(buffer,lpRect->left,lpRect->top);
	}
	break;
    case 11: // last receive
      if (!IsColumnHidden(11)) {
	if(lpPartFile->GetLastChangeDatetime()!=0) {
	  char tmpstr[512];
	  time_t kello=lpPartFile->GetLastChangeDatetime();
	  strftime(tmpstr,sizeof(tmpstr),"%A, %x, %X",
		   localtime(&kello));
	  buffer=tmpstr;//lpPartFile->GetLastChangeDatetime().Format( theApp.glob_prefs->GetDateTimeFormat());
	}
	else buffer="";
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
    }
  }
}

#define ILD_NORMAL wxIMAGELIST_DRAW_TRANSPARENT

void CDownloadListCtrl::DrawSourceItem(wxDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem) {
  if(lpRect->left < lpRect->right) { 
    
    CString buffer;
    CUpDownClient *lpUpDownClient = (CUpDownClient*)lpCtrlItem->value;
    switch(nColumn) {

    case 0:		// icon, name, status
      {
	RECT cur_rec;
	memcpy(&cur_rec, lpRect, sizeof(RECT));
	// +3 is added by OnDrawItem()... so take it off 
	POINT point = {cur_rec.left, cur_rec.top/*+1*/-2};
	if (lpCtrlItem->type == 2){
	  switch (lpUpDownClient->GetDownloadState()) {
	  case DS_CONNECTING:
	    m_ImageList.Draw(2, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_CONNECTED:
	    m_ImageList.Draw(2, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_WAITCALLBACK:
	    m_ImageList.Draw(2, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_ONQUEUE:
	    if(lpUpDownClient->IsRemoteQueueFull())
	      m_ImageList.Draw(3, *dc, point.x,point.y, ILD_NORMAL);
	    else
	      m_ImageList.Draw(1, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_DOWNLOADING:
	    m_ImageList.Draw(0, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_REQHASHSET:
	    m_ImageList.Draw(0, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_NONEEDEDPARTS:
	    m_ImageList.Draw(3, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_LOWTOLOWIP:
	    m_ImageList.Draw(3, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  case DS_TOOMANYCONNS:
	    m_ImageList.Draw(2, *dc, point.x,point.y, ILD_NORMAL);
	    break;
	  default:
	    m_ImageList.Draw(4, *dc, point.x,point.y, ILD_NORMAL);
	  }
	}
	else {
	  
	  m_ImageList.Draw(3, *dc, point.x,point.y, ILD_NORMAL);
	}
	cur_rec.left += 20;
	if (lpUpDownClient->IsFriend()){
	  POINT point2= {cur_rec.left,cur_rec.top+1};
	  m_ImageList.Draw(6, *dc, point2.x,point.y, ILD_NORMAL);
	  cur_rec.left += 20;
	}
	else if (lpUpDownClient->ExtProtocolAvailable()) {
	  POINT point2= {cur_rec.left,cur_rec.top+1};
	  m_ImageList.Draw(5,* dc, point2.x,point.y, ILD_NORMAL);
	  cur_rec.left += 20;
	}
	else{
	  POINT point2= {cur_rec.left,cur_rec.top+1};
	  if( lpUpDownClient->GetClientSoft() == SO_MLDONKEY || lpUpDownClient->GetClientSoft() == SO_NEW_MLDONKEY )
	    m_ImageList.Draw(8, *dc, point2.x,point.y, ILD_NORMAL);
	  else if ( lpUpDownClient->GetClientSoft() == SO_EDONKEYHYBRID )
	    m_ImageList.Draw(11, *dc, point2.x,point.y, ILD_NORMAL);
	  else
	    m_ImageList.Draw(7, *dc, point2.x,point.y, ILD_NORMAL);
	  cur_rec.left += 20;
	}
	
	if (!lpUpDownClient->GetUserName()) {
	  buffer = "?";
	}
	else {
	  buffer.Format("%s",lpUpDownClient->GetUserName());
	}
	dc->DrawText(buffer,cur_rec.left,cur_rec.top);
      }
      break;
      
    case 1:		// size
      break;
      
    case 2:
      if ( !IsColumnHidden(3)) {
	dc->DrawText("",lpRect->left,lpRect->top);
	break;
      }

    case 3:// completed
      if (lpCtrlItem->type == 2 && lpUpDownClient->GetTransferedDown()) {
	buffer.Format( "%s", CastItoXBytes(lpUpDownClient->GetTransferedDown()).GetData());
	//dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);	
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
      break;
      
    case 4:		// speed

      if (lpCtrlItem->type == 2 && lpUpDownClient->GetDownloadDatarate()){
	if (lpUpDownClient->GetDownloadDatarate()==0) buffer=""; else
	  buffer.Format("%.1f %s", lpUpDownClient->GetDownloadDatarate()/1024.0f,GetResString(IDS_KBYTESEC).GetData());
	//dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
      break;
      
#ifndef DISABLE_PROGRESS
    case 5: // file info
      {
	lpRect->bottom--;
	lpRect->top++;

	int iWidth = lpRect->right - lpRect->left; 
	int iHeight = lpRect->bottom - lpRect->top; 

	DWORD dwTicks=GetTickCount();
	wxMemoryDC cdcStatus;

	if(lpCtrlItem->dwUpdated+(4*DLC_BARUPDATE)<dwTicks||!lpCtrlItem->status||iWidth!=lpCtrlItem->status->GetWidth()||!lpCtrlItem->dwUpdated) {  
	  if(lpCtrlItem->status==NULL) {
	    lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
	  } else {
	    //delete lpCtrlItem->status;
	    //lpCtrlItem->status=NULL;
	    //lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
	    lpCtrlItem->status->Create(iWidth,iHeight);
	  }
	  
	  cdcStatus.SelectObject(*(lpCtrlItem->status));
	  
	  lpUpDownClient->DrawStatusBar(&cdcStatus,  wxRect(0,0,iWidth,iHeight),(lpCtrlItem->type == 3), theApp.glob_prefs->UseFlatBar()); 
	  lpCtrlItem->dwUpdated=dwTicks+(rand()%128);
	} else {
	  cdcStatus.SelectObject(*(lpCtrlItem->status));
	}
	dc->Blit(lpRect->left,lpRect->top,iWidth,iHeight,&cdcStatus,0,0);
	cdcStatus.SelectObject(wxNullBitmap);

	lpRect->top--;
	lpRect->bottom++;
      }
      break;
#endif

      
    case 6:{ 		// sources
      switch(lpUpDownClient->GetClientSoft()){
      case SO_EDONKEY:
	buffer.Format(_("eDonkey v%i"), lpUpDownClient->GetVersion());
	break;
      case SO_EDONKEYHYBRID:
	buffer.Format( _("eDonkeyHybrid v%i"), lpUpDownClient->GetVersion());
	break;
      case SO_EMULE:
      case SO_OLDEMULE:{
	buffer.Format(_("eMule v%02X"), lpUpDownClient->GetMuleVersion());
	break;
      }
      case SO_CDONKEY:{
	buffer.Format(_("cDonkey v%02X"), lpUpDownClient->GetMuleVersion());
	break;
      }
      case SO_LMULE:{
	buffer.Format(_("lMule v%02X"), lpUpDownClient->GetMuleVersion());
	break;
      }
      case SO_MLDONKEY:
	buffer.Format(_("Old MlDonkey"));
	break;
      case SO_NEW_MLDONKEY:
	buffer.Format(_("New MlDonkey"));
	break;
      case SO_UNKNOWN:
	buffer.Format( "%s", GetResString(IDS_UNKNOWN).GetData());
      }
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
    }
      
    case 7:		// prio
      if (lpUpDownClient->GetDownloadState()==DS_ONQUEUE){
	if( lpUpDownClient->IsRemoteQueueFull() ){
	  buffer.Format( "%s",GetResString(IDS_QUEUEFULL).GetData());
	  dc->DrawText(buffer,lpRect->left,lpRect->top);
	}
	else{
	  if ( lpUpDownClient->GetRemoteQueueRank()){
	    buffer.Format("QR: %u",lpUpDownClient->GetRemoteQueueRank());
	    dc->DrawText(buffer,lpRect->left,lpRect->top);
	  }
	  else{
	    buffer="";
	    dc->DrawText(buffer,lpRect->left,lpRect->top);
	  }
	}
      } else {
	buffer= "";
	dc->DrawText(buffer,lpRect->left,lpRect->top);
      }
      break;
      
    case 8:		// status
      if (lpCtrlItem->type == 2){
	switch (lpUpDownClient->GetDownloadState()) {
	case DS_CONNECTING:
	  buffer = GetResString(IDS_CONNECTING);
	  break;
	case DS_CONNECTED:
	  buffer = GetResString(IDS_ASKING);
	  break;
	case DS_WAITCALLBACK:
	  buffer = GetResString(IDS_CONNVIASERVER);
	  break;
	case DS_ONQUEUE:
	  if( lpUpDownClient->IsRemoteQueueFull() )
	    buffer = GetResString(IDS_QUEUEFULL);
	  else
	    buffer = GetResString(IDS_ONQUEUE);
	  break;
	case DS_DOWNLOADING:
	  buffer = GetResString(IDS_TRANSFERRING);
	  break;
	case DS_REQHASHSET:
	  buffer = GetResString(IDS_RECHASHSET);
	  break;
	case DS_NONEEDEDPARTS:
	  buffer = GetResString(IDS_NONEEDEDPARTS);
	  break;
	case DS_LOWTOLOWIP:
	  buffer = GetResString(IDS_NOCONNECTLOW2LOW);
	  break;
	case DS_TOOMANYCONNS:
	  buffer = GetResString(IDS_TOOMANYCONNS);
	  break;
	default:
	  buffer = GetResString(IDS_UNKNOWN);
	}
      }
      else {
	buffer = GetResString(IDS_ASKED4ANOTHERFILE);
      }
      dc->DrawText(buffer,lpRect->left,lpRect->top);
      break;
      
    case 9:		// remaining time & size
      break;
    }
  }
}


void CDownloadListCtrl::OnLvnItemActivate(wxListEvent& evt){
  //LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); 
  CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(evt.GetIndex()); //pNMIA->iItem);
  
  bool added=false;

  if (content->type == FILE_TYPE){
    CPartFile* partfile = (CPartFile*)content->value;
    
#if 0
    if (partfile->GetStatus()==PS_COMPLETE) {
      char* buffer = new char[MAX_PATH];
      sprintf(buffer,"%s",partfile->GetFullName());
      ShellOpenFile(buffer);
      delete buffer;
      *pResult = 0;
      return;
    }
#endif
#warning TODO: Open file when clicked

    if(!partfile->srcarevisible) {
      Freeze();
      
      for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
	CtrlItem_Struct* cur_item=it->second;
	
	if (cur_item->owner == partfile) {
#warning Modifier keys are missing 
	  //InsertItem(LVIF_PARAM,pNMIA->iItem+1,0,0,0,0,(LPARAM)cur_item);
	  
	  added=true;
	  CUpDownClient* client=(CUpDownClient*)cur_item->value;
	  wxString textData;
	  wxString status;
	  int imageID=-1;
	  
	  int newid=InsertItem(evt.GetIndex()+1,textData);
	  wxListItem newitem;
	  newitem.m_itemId=newid;
	  newitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
	  SetItem(newitem);
	  SetItemData(newid,(long)cur_item);
	}
	partfile->srcarevisible = added;
      }
      Thaw();
    }
    else{
      HideSources(partfile,false,false,false);
    }
  }
  //*pResult = 0;
}

bool CDownloadListCtrl::ProcessEvent(wxEvent& evt)
{
  if(evt.GetEventType()!=wxEVT_COMMAND_MENU_SELECTED)
    return CMuleListCtrl::ProcessEvent(evt);

  wxCommandEvent& event=(wxCommandEvent&)evt;
  long item=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
  if(item!=(-1)) {
    CtrlItem_Struct* content=(CtrlItem_Struct*)this->GetItemData(item);
    UINT selectedCount=this->GetSelectedItemCount();
    CTypedPtrList<CPtrList,CPartFile*> selectedList;
    item=-1;
    for(;;) {
      item=GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
      if(item==(-1))
	break;
      if (((CtrlItem_Struct*)this->GetItemData(item))->type == 1)
	selectedList.AddTail((CPartFile*)((CtrlItem_Struct*)this->GetItemData(item))->value); 
    }
    if (content->type == 1){
      CPartFile* file = (CPartFile*)content->value;
      
      switch (event.GetId())
	{
	case MP_CANCEL:
	  {
	    //for multiple selections 
	    if(selectedCount > 1)
	      { 
		Freeze();
		wxString fileList; 
		for (POSITION pos = selectedList.GetHeadPosition() ; pos != 0 ; selectedList.GetNext(pos))
		  {
		    fileList += "\n" ; 
		    fileList += selectedList.GetAt(pos)->GetFileName(); 
		  } 
		if (wxMessageBox((GetResString(IDS_Q_CANCELDL).GetData() + fileList),GetResString(IDS_CANCEL),wxICON_QUESTION|wxYES_NO) == wxYES)
		  { 
		    while(!selectedList.IsEmpty())
		      {
			HideSources(selectedList.GetHead());
			switch(selectedList.GetHead()->GetStatus()) { 
			case PS_WAITINGFORHASH: 
			case PS_HASHING: 
			case PS_COMPLETING: 
			case PS_COMPLETE: 
			  selectedList.RemoveHead(); 
			  break; 
			case PS_PAUSED:
			  selectedList.GetHead()->DeleteFile(); 
			  selectedList.RemoveHead();
			  break;
			default:
			  theApp.downloadqueue->StartNextFile();
			  selectedList.GetHead()->DeleteFile(); 
			  selectedList.RemoveHead(); 
			}
		      }  
		  }
		Thaw();
		break; 
	      }
	    // single selection
	    switch(file->GetStatus())
	      {
	      case PS_WAITINGFORHASH:
	      case PS_HASHING:
	      case PS_COMPLETING:
	      case PS_COMPLETE:
		break;
	      case PS_PAUSED:
		if (wxMessageBox(GetResString(IDS_Q_CANCELDL2),GetResString(IDS_CANCEL),wxICON_QUESTION|wxYES_NO) == wxYES) {
		  HideSources(file);
		  file->DeleteFile();
		}
		break;
	      default:
		if (wxMessageBox(GetResString(IDS_Q_CANCELDL2),GetResString(IDS_CANCEL),wxICON_QUESTION|wxYES_NO) == wxYES) {
		  HideSources(file);
		  file->DeleteFile();
		  theApp.downloadqueue->StartNextFile();
		}
		break;
	      }
	    break;
	  }
	case MP_PRIOHIGH:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
		selectedList.GetHead()->SetPriority(PR_HIGH); 
		selectedList.RemoveHead(); 
	      } 
	      Thaw();
	      break; 
	    } 
	  file->SetAutoPriority(false);
	  file->SetPriority(PR_HIGH);
	  break;
	case MP_PRIOLOW:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
		selectedList.GetHead()->SetPriority(PR_LOW); 
		selectedList.RemoveHead(); 
	      } 
	      Thaw();
	      break; 
	    } 
	  file->SetAutoPriority(false);
	  file->SetPriority(PR_LOW);
	  break;
	case MP_PRIONORMAL:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
		selectedList.GetHead()->SetPriority(PR_NORMAL); 
		selectedList.RemoveHead(); 
	      }
	      Thaw();
	      break; 
	    } 
	  file->SetAutoPriority(false);
	  file->SetPriority(PR_NORMAL);
	  break;
	case MP_PRIOAUTO:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) {
		selectedList.GetHead()->SetPriority(PR_AUTO);
		selectedList.RemoveHead();
	      }
	      Thaw();
	      break;
	    } 
	  file->SetPriority(PR_AUTO);
	  break;
	case MP_PAUSE:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		selectedList.GetHead()->PauseFile(); 
		selectedList.RemoveHead(); 
	      } 
	      Thaw();
	      break; 
	    } 
	  file->PauseFile();
	  break;
	case MP_RESUME:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		selectedList.GetHead()->ResumeFile(); 
		selectedList.GetHead()->SavePartFile(); 
		selectedList.RemoveHead(); 
	      }
	      Thaw();
	      break; 
	    } 
	  file->ResumeFile();
	  file->SavePartFile();
	  break;
	case MP_STOP:
	  if(selectedCount > 1)
	    {
	      Freeze();
	      while(!selectedList.IsEmpty()) { 
		CPartFile *selected = selectedList.GetHead();
		HideSources(selected);
		selected->StopFile(); 
		selectedList.RemoveHead(); 
	      }
	      Thaw();
	      break; 
	    } 
	  HideSources(file);
	  file->StopFile();
	  break;
	case MP_CLEARCOMPLETED:
	  Freeze();
	  ClearCompleted();
	  Thaw();
	  break;
	case MP_METINFO:
	  {
	    CFileDetailDialog* dialog=new CFileDetailDialog(this,file);
	    dialog->ShowModal();
	    delete dialog;
	    break;
	  }
	case MP_GETED2KLINK:
	  if(selectedCount > 1)
	    {
	      wxString str;
	      while(!selectedList.IsEmpty()) { 
		str += theApp.CreateED2kLink(selectedList.GetHead()) + "\n"; 
		selectedList.RemoveHead(); 
	      }
	      theApp.CopyTextToClipboard(str);
	      break; 
	    } 
	  theApp.CopyTextToClipboard(theApp.CreateED2kLink(file));
	  break;
	case MP_GETHTMLED2KLINK:
	  if(selectedCount > 1)
	    {
	      CString str;
	      while(!selectedList.IsEmpty()) { 
		str += theApp.CreateHTMLED2kLink(selectedList.GetHead()) + "\n"; 
		selectedList.RemoveHead(); 
	      }
	      theApp.CopyTextToClipboard(str);
	      break; 
	    } 
	  theApp.CopyTextToClipboard(theApp.CreateHTMLED2kLink(file));
	  break;
	case MP_OPEN:{
	  if(selectedCount > 1)
	    break;
	  char* buffer = new char[250];
	  sprintf(buffer,"%s/%s",theApp.glob_prefs->GetIncomingDir(),file->GetFileName());
	  //ShellOpenFile(buffer);
	  printf("===> open %s\n",buffer);
	  delete buffer;
	  break; 
	}
	case MP_PREVIEW:{
	  if(selectedCount > 1)
	    break;
	  file->PreviewFile();
	  break;
	}
	case MP_VIEWFILECOMMENTS:{
	  CCommentDialogLst dialog(this,file); 
	  dialog.ShowModal();
	  break;
	}
	}
    }
    else{
      CUpDownClient* client = (CUpDownClient*)content->value;
      switch (event.GetId()){
      case MP_SHOWLIST:
	client->RequestSharedFileList();
	break;	
      case MP_MESSAGE:
	theApp.emuledlg->chatwnd->StartSession(client);
	break;
      case MP_ADDFRIEND:{
//        int pos=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
//        while (pos!=-1){
  	  theApp.friendlist->AddFriend(client);
//	  pos=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
//	}
	break;
      }
      case MP_DETAIL:
	CClientDetailDialog* dialog=new CClientDetailDialog(this,client);
	dialog->ShowModal();
	delete dialog;
	//dialog.DoModal();
	return TRUE;
	break;
      }
    }
    // cleanup multiselection
    selectedList.RemoveAll();    
  } else {
    // nothing selected
    switch(event.GetId()) {
    case MP_CLEARCOMPLETED:
      ClearCompleted();
      break;
    }
  }

  // should we call this? (no!)
  return CMuleListCtrl::ProcessEvent(evt);
}


void CDownloadListCtrl::OnColumnClick( wxListEvent& evt) {//NMHDR* pNMHDR, LRESULT* pResult){
  //NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  
  // Barry - Store sort order in preferences
  // Determine ascending based on whether already sorted on this column
  int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableDownload);
  bool m_oldSortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableDownload);
  bool sortAscending = (sortItem != evt.GetColumn()) ? true : !m_oldSortAscending;
  // Item is column clicked
  sortItem = evt.GetColumn();
  // Save new preferences
  theApp.glob_prefs->SetColumnSortItem(CPreferences::tableDownload, sortItem);
  theApp.glob_prefs->SetColumnSortAscending(CPreferences::tableDownload, sortAscending);
  // Sort table
  SetSortArrow(sortItem, sortAscending);
  SortItems(SortProc, sortItem + (sortAscending ? 0:100));
}

int CDownloadListCtrl::SortProc(long lParam1, long lParam2, long lParamSort){
	CtrlItem_Struct* item1 = (CtrlItem_Struct*)lParam1;
	CtrlItem_Struct* item2 = (CtrlItem_Struct*)lParam2;

	int sortMod = 1;
	if(lParamSort >= 100) {
		sortMod = -1;
		lParamSort -= 100;
	}

	int comp;

	if(item1->type == 1 && item2->type != 1) {
		if(item1->value == item2->parent->value)
			return -1;

		comp = Compare((CPartFile*)item1->value, (CPartFile*)(item2->parent->value), lParamSort);

	} else if(item2->type == 1 && item1->type != 1) {
		if(item1->parent->value == item2->value)
			return 1;

		comp = Compare((CPartFile*)(item1->parent->value), (CPartFile*)item2->value, lParamSort);

	} else if (item1->type == 1) {
		CPartFile* file1 = (CPartFile*)item1->value;
		CPartFile* file2 = (CPartFile*)item2->value;

		comp = Compare(file1, file2, lParamSort);

	} else {
		CUpDownClient* client1 = (CUpDownClient*)item1->value;
		CUpDownClient* client2 = (CUpDownClient*)item2->value;
		comp = Compare((CPartFile*)(item1->parent->value), (CPartFile*)(item2->parent->value), lParamSort);
		if(comp != 0)
			return sortMod * comp;
		if (item1->type != item2->type)
			return item1->type - item2->type;

		comp = Compare(client1, client2, lParamSort,sortMod);
	}

	return sortMod * comp;
}

#if 0
void CDownloadListCtrl::ClearCompleted(){
   POSITION pos1, pos2;
   for( pos1 = listcontent.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
	   listcontent.GetNext(pos1);
	   CtrlItem_Struct* delitem = listcontent.GetAt(pos2);
	   if (delitem->type == 1){
		   CPartFile* file = (CPartFile*)delitem->value;
		   if (!file->IsPartFile())
			   RemoveFile(file);
	   }
   }
}

void CDownloadListCtrl::SetStyle() {
	if (theApp.glob_prefs->IsDoubleClickEnabled())
		SetExtendedStyle(LVS_EX_FULLROWSELECT);
	else
		SetExtendedStyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT);
}

void CDownloadListCtrl::OnListModified(NMHDR *pNMHDR, LRESULT *pResult) {
	NM_LISTVIEW *pNMListView = (NM_LISTVIEW*)pNMHDR;

	//this works because true is equal to 1 and false equal to 0
	BOOL notLast = pNMListView->iItem + 1 != GetItemCount();
	BOOL notFirst = pNMListView->iItem != 0;
	RedrawItems(pNMListView->iItem - notFirst, pNMListView->iItem + notLast);
}
#endif

int CDownloadListCtrl::Compare(CPartFile* file1, CPartFile* file2, long lParamSort) {
	switch(lParamSort){
	case 0: //filename asc
		return strcmpi(file1->GetFileName(),file2->GetFileName());
	case 1: //size asc
		return file1->GetFileSize() - file2->GetFileSize();
	case 2: //transfered asc
		return file1->GetTransfered() - file2->GetTransfered();
	case 3: //completed asc
		return file1->GetCompletedSize() - file2->GetCompletedSize();
	case 4: //speed asc
		return file1->GetDatarate() - file2->GetDatarate();
	case 5: //progress asc
		{
			float comp = file1->GetPercentCompleted() - file2->GetPercentCompleted();
			if(comp > 0)
				return 1;
			else if(comp < 0)
				return -1;
			else
				return 0;
		}
	case 6: //sources asc
		return file1->GetSourceCount() - file2->GetSourceCount();
	case 7: //priority asc
		return file1->GetPriority() - file2->GetPriority();
	case 8: //Status asc 
		return file1->getPartfileStatusRang()-file2->getPartfileStatusRang(); 
	case 9: //Remaining Time asc 
		return file1->getTimeRemaining()-file2->getTimeRemaining() ;
	case 10: //Remaining Time asc 
		if (file1->lastseencomplete > file2->lastseencomplete)
			return 1;
		else if(file1->lastseencomplete < file2->lastseencomplete)
			return -1;
		else
			return 0;
	default:
		return 0;
	}
}

int CDownloadListCtrl::Compare(CUpDownClient *client1, CUpDownClient *client2, long lParamSort, int sortMod) {
	switch(lParamSort){
	case 0: //name asc
		if(client1->GetUserName() == client2->GetUserName())
			return 0;
		else if(!client1->GetUserName())
			return 1;
		else if(!client2->GetUserName())
			return -1;
		return strcmpi(client1->GetUserName(),client2->GetUserName());
	case 1: //size but we use status asc
		return client1->GetDownloadState() - client2->GetDownloadState();
	case 2://transfered asc
	case 3://completed asc
		return client1->GetTransferedDown() - client2->GetTransferedDown();
	case 4: //speed asc
		return client1->GetDownloadDatarate() - client2->GetDownloadDatarate();
	case 5: //progress asc
		return client1->GetAvailablePartCount() - client2->GetAvailablePartCount();
	case 6:
		if( client1->GetClientSoft() == client2->GetClientSoft() ){
			if( client1->IsEmuleClient() )
				return client2->GetMuleVersion() - client1->GetMuleVersion();
			else
				return client2->GetVersion() - client1->GetVersion();
		}
		return client1->GetClientSoft() - client2->GetClientSoft();
	case 7: //qr asc
		if(client1->GetRemoteQueueRank() == 0 && client1->GetDownloadState() == DS_ONQUEUE && client1->IsRemoteQueueFull() == true) return 1;
		if(client2->GetRemoteQueueRank() == 0 && client2->GetDownloadState() == DS_ONQUEUE && client2->IsRemoteQueueFull() == true) return -1;
		if(client1->GetRemoteQueueRank() == 0) return 1;
		if(client2->GetRemoteQueueRank() == 0) return -1;
		return client1->GetRemoteQueueRank() - client2->GetRemoteQueueRank();
	case 8:
		if( client1->GetDownloadState() == client2->GetDownloadState() ){
			if( client1->IsRemoteQueueFull() )
				return 1;
			if( client2->IsRemoteQueueFull() )
				return -1;
			else
				return 0;
		}
		return client1->GetDownloadState() - client2->GetDownloadState();
	default:
		return 0;
	}
}


void CDownloadListCtrl::CreateMenues() {
#if 0
	if (m_PrioMenu) m_PrioMenu.DestroyMenu();
	if (m_FileMenu) m_FileMenu.DestroyMenu();

	m_PrioMenu.CreateMenu();
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIOLOW,GetResString(IDS_PRIOLOW));
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIONORMAL,GetResString(IDS_PRIONORMAL));
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIOHIGH, GetResString(IDS_PRIOHIGH));

	m_FileMenu.CreatePopupMenu();
	m_FileMenu.AddMenuTitle(GetResString(IDS_DOWNLOADMENUTITLE));
	m_FileMenu.AppendMenu(MF_STRING|MF_POPUP,(UINT_PTR)m_PrioMenu.m_hMenu, GetResString(IDS_PRIORITY) );

	m_FileMenu.AppendMenu(MF_STRING,MP_CANCEL,GetResString(IDS_MAIN_BTN_CANCEL) );
	m_FileMenu.AppendMenu(MF_STRING,MP_STOP, GetResString(IDS_DL_STOP));
	m_FileMenu.AppendMenu(MF_STRING,MP_PAUSE, GetResString(IDS_DL_PAUSE));
	m_FileMenu.AppendMenu(MF_STRING,MP_RESUME, GetResString(IDS_DL_RESUME));
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING,MP_OPEN, GetResString(IDS_DL_OPEN) );//<--9/21/02
	m_FileMenu.AppendMenu(MF_STRING,MP_PREVIEW, GetResString(IDS_DL_PREVIEW) );
	m_FileMenu.AppendMenu(MF_STRING,MP_METINFO, GetResString(IDS_DL_INFO) );//<--9/21/02
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING,MP_CLEARCOMPLETED, GetResString(IDS_DL_CLEAR));
	m_FileMenu.AppendMenu(MF_STRING,MP_GETED2KLINK, GetResString(IDS_DL_LINK1) );
	m_FileMenu.AppendMenu(MF_STRING,MP_GETHTMLED2KLINK, GetResString(IDS_DL_LINK2));
	SetMenu(&m_FileMenu);
#endif
}

wxString CDownloadListCtrl::getTextList() {
	wxString out="";

	// Search for file(s)
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){ // const is better
		CtrlItem_Struct* cur_item = it->second;
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);

			theApp.emuledlg->AddLogLine(false, file->GetFileName());

			char buffer[50+1];
			//ASSERT(file->GetFileName() != NULL);
			strncpy(buffer, file->GetFileName(), 50);
			buffer[50] = '\0';

			wxString temp;
			temp.Printf("\n%s\t [%.1f%%] %i/%i - %s",
						buffer,
						file->GetPercentCompleted(),
						file->GetTransferingSrcCount(),
						file->GetSourceCount(), 
						file->getPartfileStatus().GetData());
			
			out += temp;
		}
	}

	theApp.emuledlg->AddLogLine(false, out);
	return out;
}

void CDownloadListCtrl::ClearCompleted(){
	// Search for completed file(s)
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* cur_item = it->second;
		it++; // Already point to the next iterator. 
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			if(file->IsPartFile() == false){
				RemoveFile(file);
			}
		}
	}
}

void CDownloadListCtrl::ShowFilesCount() {
	CString counter;
	uint16	count=0;//theApp.downloadqueue->GetFileCount();

	// remove all displayed files with a different cat
	for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
		const CtrlItem_Struct* cur_item = it->second;
		if (cur_item->type == FILE_TYPE){
			CPartFile* file=(CPartFile*)cur_item->value;
			if (file->GetCategory()==curTab || 
				(!theApp.glob_prefs->ShowAllNotCats() && file->GetCategory()>0 && curTab==0 ) ) count++;
		}
	}

	wxString fmtstr = wxString::Format(_("Downloads (%i)"), GetItemCount());
	wxStaticCast(FindWindowByName(wxT("downloadsLabel")),wxStaticText)->SetLabel(fmtstr); 
}

void CDownloadListCtrl::ShowSelectedFileDetails() {
	if (GetSelectedItemCount()==0) return;
	int cursel=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
	CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(cursel);

	if (content->type == FILE_TYPE){
		CPartFile* file = (CPartFile*)content->value;
		//CFileDetailDialog dialog(file);
		//dialog.DoModal();

		if ((file->HasComment() || file->HasRating()) /*&& p.x<13*/ ) {
#warning Need mouse position somehow
		  //CCommentDialogLst dialog(file);
		  //dialog.DoModal();
#warning CommentDialog should be shown here
		}
		else {
		  //CFileDetailDialog dialog(file);
		  //dialog.DoModal();
#warning Filedetail dialog should be shown here
		}

	}else {
		CUpDownClient* client = (CUpDownClient*)content->value;
		//CClientDetailDialog dialog(client);
		//dialog.DoModal();
		printf("Show details me too\n");
	}
}

void CDownloadListCtrl::ChangeCategory(int newsel){

  //SetRedraw(FALSE);
  Freeze();

	// remove all displayed files with a different cat and show the correct ones
	for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
		const CtrlItem_Struct* cur_item = it->second;
		if (cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			
			if ( (newsel>0 && newsel!=file->GetCategory()) ||
				(theApp.glob_prefs->ShowAllNotCats() && newsel==0 && file->GetCategory()>0)        ) HideFile(file);
			else if (ShowItemInCurrentCat(file,newsel)) ShowFile(file);

		}
	}

	//SetRedraw(TRUE);
	Thaw();
	curTab=newsel;
	ShowFilesCount();
}

void CDownloadListCtrl::HideFile(CPartFile* tohide){
	HideSources(tohide);

	// Retrieve all entries matching the source
	std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(tohide);
	for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
		CtrlItem_Struct* updateItem  = it->second;

		// Find entry in CListCtrl and update object
 		//LVFINDINFO find;
		//find.flags = LVFI_PARAM;
		//find.lParam = (LPARAM)updateItem;
		sint16 result = FindItem(-1,(long)updateItem);
		if(result != (-1)) {
			DeleteItem(result);
			return;
		}
	}
}

void CDownloadListCtrl::ShowFile(CPartFile* toshow){
	// Retrieve all entries matching the source
	std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(toshow);
	for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
		CtrlItem_Struct* updateItem  = it->second;

		// Check if entry is already in the List
 		//LVFINDINFO find;
		//find.flags = LVFI_PARAM;
		//find.lParam = (LPARAM)updateItem;
		sint16 result = FindItem(-1,(long)updateItem);
		if(result == (-1)) {
		  //InsertItem(LVIF_PARAM,GetItemCount(),0,0,0,0,(LPARAM)updateItem);
		  int newitem=InsertItem(GetItemCount(),"This is not visible");
		  SetItemData(newitem,(long)updateItem);

		  wxListItem myitem;
		  myitem.m_itemId=newitem;
		  myitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));	
		  SetItem(myitem);
		}
		return;
	}
}

bool CDownloadListCtrl::ShowItemInCurrentCat(CPartFile* file,int newsel) {
	return ( ((newsel==0 && !theApp.glob_prefs->ShowAllNotCats()) || 
		(newsel==0 && theApp.glob_prefs->ShowAllNotCats() && file->GetCategory()==0 )) ||
		( newsel>0 && newsel==file->GetCategory() ) );
}
